#ifndef __THREADMANFORUSER_H
#define __THREADMANFORUSER_H

#include "../hle.h"
#include "../host/host.h"

#include "__ModuleMgrForUser.h"

#define KERNELOBJECT_MAX_NAME_LENGTH 31

#define MAX_THREAD 255

/** Event flag wait types */
enum PspEventFlagWaitTypes
{
    /** Wait for all bits in the pattern to be set */
    PSP_EVENT_WAITAND = 0,
    /** Wait for one or more bits in the pattern to be set */
    PSP_EVENT_WAITOR  = 1,
    /** Clear the wait pattern when it matches */
    PSP_EVENT_WAITCLEAR = 0x20
};

typedef struct
{
    u32 lo;
    u32 hi;
} SceKernelSysClock;

enum ThreadStatus
{
    THREADSTATUS_RUN = 1,
    THREADSTATUS_READY = 2,
    THREADSTATUS_WAIT = 4,
    THREADSTATUS_SUSPEND = 8,
    THREADSTATUS_DORMANT = 16,
    THREADSTATUS_DEAD = 32
};

typedef enum /* fake values */
{
    WAITTYPE_NONE = 0,
    WAITTYPE_SLEEP,
    WAITTYPE_DELAY,
    WAITTYPE_SEMA,
    WAITTYPE_EVENTFLAG,
    WAITTYPE_MBX,
    WAITTYPE_VPL,
    WAITTYPE_FPL,
    WAITTYPE_THREADEND,
    WAITTYPE_AUDIOCHANNEL,
    WAITTYPE_UMD,
    WAITTYPE_DRAWSYNC,
    WAITTYPE_MUTEX,
    WAITTYPE_LWMUTEX
} WaitType;

typedef struct
{
    u32 r[32];
    float f[32];
    float v[128];
    u32 vfpuCtrl[15];

    u32 hi;
    u32 lo;
    u32 pc;
    u32 fcsr;
} Context;

typedef struct
{
    u32 nativeSize;
    char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];

    /* Threading stuff */
    u32 attr;
    u32 status;
    u32 entrypoint;
    u32 initialStack;
    u32 stackSize;
    u32 gpreg;

    u32 initialPriority;
    u32 currentPriority;
    WaitType waitType;
    SceUID waitID;
    s32 wakeupCount;
    u32 exitStatus;
    SceKernelSysClock runForClocks;
    u32 numInterruptPreempts;
    u32 numThreadPreempts;
    u32 numReleases;
} NativeThread;

typedef struct
{
    NativeThread nt;

    u32 waitValue;
    s32 sleeping;

    u8 isProcessingCallbacks;

    Context context;

    SceUID *callbacks;

    u32 stackaddr;

    Timer delayTimer;
    u8 delayed;
} SceKernelThread;

typedef struct
{
    SceUInt size;
    char name[32];
    u32 entrypoint;
    SceUID threadId;

    u32 notifyCount;
    u32 notifyArg;
    u32 argumentAddr;

    SceUInt     attr;
    SceUInt     initPattern;
    SceUInt     currentPattern;
    s32         numWaitThreads;
} Callback;

typedef enum
{
    RESUMETYPE_NONE,
    RESUMETYPE_ID,
    RESUMETYPE_VALUE_OR
} ResumeType;

typedef enum
{
    CBTYPE_EXIT,
    CBTYPE_MS,
    CBTYPE_UMD,
    CBTYPE_GE_FINISH,
    CBTYPE_GE_SIGNAL,
    NUM_CBTYPES
} CbType;

#define LWMUTEX_ATTR_RECURSIVE 0x200

typedef struct
{
    char name[32];
    u32 attr;
    u32 uid;
    u32 workAddr;
    u32 initCount;
    u32 curCount;
    u32 numWaitThreads;
} NativeLwMutex;

typedef struct
{
    NativeLwMutex n;
    SceUID thread;
    SceUID waitingThreads[MAX_THREAD]; /* TODO: STATIC */
} LwMutex;

/*************
 * CALLBACKS *
 *************/

SceUID __KernelCreateCallback(char *name, u32 entrypoint, u32 argument);

void __KernelSetCbRetAddr(u32 addr);
void __KernelReturnFromCallback();
u8 __KernelInCallback();

void __KernelCallCallback(SceUID id);
void __KernelCallCallbackWithArg(SceUID id, u32 arg);
void __KernelCallCallbackType(CbType type);
void __KernelCallCallbackTypeWithArg(CbType type, u32 arg);

#ifndef NO_THREAD
void __KernelCallCallbackFromGfxTh(SceUID id);
void __KernelCallCallbackWithArgFromGfxTh(SceUID id, u32 arg);
void __KernelCallCallbackTypeFromGfxTh(CbType type);
void __KernelCallCallbackTypeWithArgFromGfxTh(CbType type, u32 arg);
void __KernelWaitCallbackEndFromGfxTh();
#endif

void __KernelNotifyCallbackType(CbType type, u32 arg);
void __KernelRegisterCallback(CbType type, SceUID cbid);
void __KernelUnregisterCallback(CbType type);
u8 __KernelCallbackExists(SceUID id);

/***************
 * EVENT FLAGS *
 ***************/

u8 __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr);

/***********
 * THREADS *
 ***********/

void __KernelThreadingInit();
void __KernelThreadingShutdown();

SceUID __KernelGetCurThread();

SceUID __KernelCreateThread(const char *name, u32 entryPoint, u32 priority, u32 stacksize, u32 attr);
u32 __KernelDeleteThread(SceUID id);
u8 __KernelResumeThreads(WaitType type, s32 id, ResumeType test);
u8 __KernelResumeDelayedThreads();
void __KernelResumeThread(SceUID threadID);
void __KernelWaitCurThread(WaitType type, SceUID id, u32 value, u32 timeout, u8 processCallbacks);
void __KernelDelayCurThread(u32 usec, u8 processCallbacks);

u32 __KernelGetWaitValue(SceUID threadID);
void __KernelReSchedule(void);

void __KernelSetupRootThread(SceUID moduleId, s32 args, const char *argp, s32 prio, u32 stacksize, s32 attr); /* represents the real PSP elf loader, run before execution */
void __KernelSetThRetAddr(u32 addr);
void __KernelReturnFromThread();

void __KernelSaveContext(Context *c);
void __KernelLoadContext(Context *c);

#endif

